/**
 * ****************************************************************************
 * Project: RoboBridge
 * ----------------------------------------------------------------------------
 * -------------------------- [c]2012 - KrawallMann ---------------------------
 * ---------------------- http://www.krawallmann.de.tc/ -----------------------
 * ****************************************************************************
 * File: AbstractAckAwaitingProtocolState.java Package:
 * tc.de.krawallmann.robobridge.communication.protocol Version: 1.0 Author(s):
 * KrawallMann
 * ****************************************************************************
 * Description:
 * ****************************************************************************
 */
package tc.de.krawallmann.robobridge.communication.protocol;

import java.util.logging.Level;

import tc.de.krawallmann.robobridge.communication.Command;
import tc.de.krawallmann.robobridge.communication.CommandType;
import tc.de.krawallmann.robobridge.util.Log;

/**
 * The Class AbstractAckAwaitingProtocolState waits for an ACK and terminates.
 * 
 * @author KrawallMann
 * @version 1.0
 * @param <T>
 *            the generic type
 */
public abstract class AbstractAckAwaitingProtocolState<T extends AbstractProtocol> extends
		AbstractProtocolState<T> {

	/** The ack. */
	private transient boolean ack;

	/** The attempts. */
	private transient int attempts = 1;

	/** The max attempts. */
	private transient final int maxAttempts;

	/**
	 * Instantiates a new abstract ack awaiting protocol state.
	 * 
	 * @param protocol
	 *            the protocol
	 * @param maxAttempts
	 *            the max attempts
	 */
	public AbstractAckAwaitingProtocolState(final T protocol, final int maxAttempts) {
		super(protocol);
		this.maxAttempts = maxAttempts;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocolState
	 * #isComplete()
	 */
	@Override
	public boolean isComplete() {
		return ack;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocolState
	 * #isFailed()
	 */
	@Override
	public boolean isFailed() {
		return super.isFailed() && !ack && attempts > maxAttempts;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocolState
	 * #onCommandReceived(basepackage.Command)
	 */
	@Override
	public boolean onCommandReceived(final Command command) {
		if (command.getType() != CommandType.ACK) {
			return false;
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocolState
	 * #update()
	 */
	@Override
	public void update() {
		try {
			if (attempts != 0) {
				Log.log(Level.WARNING, getClass(), "update",
						"Ack not received - trying again ({0}/{1} attempts)!", attempts,
						maxAttempts);
			}
			protocol.sendCommand(generateCommand());
			// Waits for ACK automagically
			ack = true;
		} catch (final InterruptedException e) {
			++attempts;
			if (attempts > maxAttempts) {
				throw new IllegalStateException("ACK not received!");
			}
		}
	}

	/**
	 * Generate command.
	 * 
	 * @return the command
	 */
	protected abstract Command generateCommand();
}
